package edu.unf.cen6940.dao;

import edu.unf.cen6940.domain.Category;
import edu.unf.cen6940.domain.Generatedproduct;
import edu.unf.cen6940.domain.Thesis;
import edu.unf.cen6940.domain.Thesiscommittee;
import edu.unf.cen6940.domain.Thesisstatus;
import edu.unf.cen6940.domain.Thesistool;

import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;

import org.skyway.spring.util.dao.AbstractJpaDao;

import org.springframework.dao.DataAccessException;

import org.springframework.stereotype.Repository;

import org.springframework.transaction.annotation.Transactional;

import com.sun.org.apache.xpath.internal.Expression;

/**
 * DAO to manage Thesis entities.
 * 
 */
@Repository("ThesisDAO")
@Transactional
public class ThesisDAOImpl extends AbstractJpaDao<Thesis> implements ThesisDAO {

	/**
	 * Set of entity classes managed by this DAO.  Typically a DAO manages a single entity.
	 *
	 */
	private final static Set<Class<?>> dataTypes = new HashSet<Class<?>>(Arrays.asList(new Class<?>[] { Thesis.class }));

	/**
	 * EntityManager injected by Spring for persistence unit rms
	 *
	 */
	@PersistenceContext(unitName = "rms")
	private EntityManager entityManager;

	/**
	 * Instantiates a new ThesisDAOImpl
	 *
	 */
	public ThesisDAOImpl() {
		super();
	}

	/**
	 * Get the entity manager that manages persistence unit 
	 *
	 */
	public EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * Returns the set of entity classes managed by this DAO.
	 *
	 */
	public Set<Class<?>> getTypes() {
		return dataTypes;
	}

	/**
	 * JPQL Query - findThesisByDefenseAttempts
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByDefenseAttempts(Integer defenseAttempts) throws DataAccessException {

		return findThesisByDefenseAttempts(defenseAttempts, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByDefenseAttempts
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByDefenseAttempts(Integer defenseAttempts, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByDefenseAttempts", startResult, maxRows, defenseAttempts);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findAllThesiss
	 *
	 */
	@Transactional
	public Set<Thesis> findAllThesiss() throws DataAccessException {

		return findAllThesiss(-1, -1);
	}

	/**
	 * JPQL Query - findAllThesiss
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findAllThesiss(int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findAllThesiss", startResult, maxRows);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}
	
	/**
	 * JPQL Query - findAllThesiss
	 *
	 */
	@Transactional
	public int getCount() throws DataAccessException {

		return getCount(-1, -1);
	}

	/**
	 * JPQL Query - findAllThesiss
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public int getCount(int startResult, int maxRows) throws DataAccessException {
		Query query1 = createNamedQuery("getThesisCount", -1, -1);
		int result = ((Long)query1.getSingleResult()).intValue();
		return result;
		
	}

	/**
	 * JPQL Query - findThesisByTitleContaining
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByTitleContaining(String title) throws DataAccessException {
		Set<Thesis> thesisSet = findThesisByTitleContaining(title, -1, -1);
		return thesisSet;
	}

	/**
	 * JPQL Query - findThesisByTitleContaining
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByTitleContaining(String title, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByTitleContaining", startResult, maxRows, "%" +title + "%");
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByAbstract_Containing
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByAbstract_Containing(String abstract_) throws DataAccessException {

		return findThesisByAbstract_Containing(abstract_, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByAbstract_Containing
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByAbstract_Containing(String abstract_, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByAbstract_Containing", startResult, maxRows, abstract_);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByThesisId
	 *
	 */
	@Transactional
	public Thesis findThesisByThesisId(Integer thesisId) throws DataAccessException {

		return findThesisByThesisId(thesisId, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByThesisId
	 *
	 */

	@Transactional
	public Thesis findThesisByThesisId(Integer thesisId, int startResult, int maxRows) throws DataAccessException {
		try {
			Query query = createNamedQuery("findThesisByThesisId", startResult, maxRows, thesisId);
			return (edu.unf.cen6940.domain.Thesis) query.getSingleResult();
		} catch (NoResultException nre) {
			return null;
		}
	}

	/**
	 * JPQL Query - findThesisByDoi
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByDoi(String doi) throws DataAccessException {

		return findThesisByDoi(doi, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByDoi
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByDoi(String doi, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByDoi", startResult, maxRows, doi);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByStartDate
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByStartDate(java.util.Calendar startDate) throws DataAccessException {

		return findThesisByStartDate(startDate, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByStartDate
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByStartDate(java.util.Calendar startDate, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByStartDate", startResult, maxRows, startDate);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByDoiContaining
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByDoiContaining(String doi) throws DataAccessException {

		return findThesisByDoiContaining(doi, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByDoiContaining
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByDoiContaining(String doi, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByDoiContaining", startResult, maxRows, doi);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByProposalAttempts
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByProposalAttempts(Integer proposalAttempts) throws DataAccessException {

		return findThesisByProposalAttempts(proposalAttempts, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByProposalAttempts
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByProposalAttempts(Integer proposalAttempts, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByProposalAttempts", startResult, maxRows, proposalAttempts);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByPrimaryKey
	 *
	 */
	@Transactional
	public Thesis findThesisByPrimaryKey(Integer thesisId) throws DataAccessException {

		return findThesisByPrimaryKey(thesisId, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByPrimaryKey
	 *
	 */

	@Transactional
	public Thesis findThesisByPrimaryKey(Integer thesisId, int startResult, int maxRows) throws DataAccessException {
		try {
			Query query = createNamedQuery("findThesisByPrimaryKey", startResult, maxRows, thesisId);
			return (edu.unf.cen6940.domain.Thesis) query.getSingleResult();
		} catch (NoResultException nre) {
			return null;
		}
	}

	/**
	 * JPQL Query - findThesisByTitle
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByTitle(String title) throws DataAccessException {

		return findThesisByTitle(title, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByTitle
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByTitle(String title, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByTitle", startResult, maxRows, title);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * JPQL Query - findThesisByAbstract_
	 *
	 */
	@Transactional
	public Set<Thesis> findThesisByAbstract_(String abstract_) throws DataAccessException {

		return findThesisByAbstract_(abstract_, -1, -1);
	}

	/**
	 * JPQL Query - findThesisByAbstract_
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set<Thesis> findThesisByAbstract_(String abstract_, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findThesisByAbstract_", startResult, maxRows, abstract_);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}
	
	/**
	 * Advanced search function 
	 */
	@Transactional
	public Set<Thesis> advancedSearch(Thesis thesis) throws DataAccessException{
		return advancedSearch(thesis, null);
	}
	/**
	 * Advanced search function 
	 */
	@Transactional
	public Set<Thesis> advancedSearch(Thesis thesis, Date endDate) throws DataAccessException{
		System.out.println("category: "+thesis.getCategories()+"+");
		CriteriaBuilder cb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Thesis> cq = cb.createQuery(Thesis.class);

		Root<Thesis> from = cq.from(Thesis.class);
		cq.select(from);
		
		List<Predicate> predicateList = new ArrayList<Predicate>();
		if(thesis.getStudent() != null){
			Predicate p = cb.equal(from.<String>get("student"), thesis.getStudent() );
			predicateList.add(p);
		}
		if(thesis.getTitle() != null && !thesis.getTitle().equals("")){
			Predicate p = cb.like(from.<String>get("title"), "%"+thesis.getTitle()+"%");
			predicateList.add(p);
		}
		if(thesis.getAbstract_() != null && !thesis.getAbstract_().equals("")){
			Predicate p = cb.like(from.<String>get("abstract_"), "%"+thesis.getAbstract_()+"%");
			predicateList.add(p);
		}
		if(thesis.getStartDate() != null){
			Predicate p = cb.greaterThanOrEqualTo(from.<Date>get("startDate"), thesis.getStartDate().getTime());
			predicateList.add(p);
		}
		if(endDate != null){
			Join<Thesis, Thesisstatus> statuses =  from.join("thesisstatuses");
			//for(Thesisstatus ts: thesis.getThesisstatuses()){
				Predicate p = cb.lessThanOrEqualTo(statuses.<Date>get("defenseDate"), endDate);
				predicateList.add(p);
			//}
		}
		if(thesis.getCategories() != null && thesis.getCategories().size() > 0){
			Join<Thesis, Category> categories =  from.join("categories");
			for(Category category: thesis.getCategories()){
			
				Predicate p = cb.equal(categories.get("categoryId"), category.getCategoryId() );
				predicateList.add(p);
			}
		}
		if(thesis.getThesistools() != null && thesis.getThesistools().size() > 0){
			Join<Thesis, Thesistool> categories =  from.join("thesistools");
			for(Thesistool thesistool : thesis.getThesistools()){
			
				Predicate p = cb.equal(categories.get("thesisToolId"), thesistool.getThesisToolId() );
				predicateList.add(p);
			}
		}
		if(thesis.getGeneratedproducts() != null && thesis.getGeneratedproducts().size() > 0){
			Join<Thesis, Generatedproduct> categories =  from.join("generatedproducts");
			for(Generatedproduct generatedproduct : thesis.getGeneratedproducts()){
			
				Predicate p = cb.equal(categories.get("generatedProductId"), generatedproduct.getGeneratedProductId() );
				predicateList.add(p);
			}
		}
		if(thesis.getThesiscommittees() != null && thesis.getThesiscommittees().size() > 0){
			//Thesiscommittee thesiscommittee = thesis.getThesiscommittees();
			
			Join<Thesis, Thesiscommittee> categories =  from.join("thesiscommittees");
			for(Thesiscommittee thesiscommittee : thesis.getThesiscommittees()){
			
				//Query query = createQuery("select myThesis from Thesis myThesis join myThesis.thesiscommittees as mythesiscommittee where mythesiscommittee.facultyByChair.facultyId = ?1", -1, -1,  thesiscommittee.getThesisCommitteeId());
				Predicate p = cb.equal(categories.get("facultyByChair"), thesiscommittee.getFacultyByChair() );
				predicateList.add(p);
			}
		}
		Predicate[] predicates = new Predicate[predicateList.size()];
	    predicateList.toArray(predicates);
		cq.where(predicates);
		TypedQuery<Thesis> typedQuery = entityManager.createQuery(cq);
		//List<Object> olist = typedQuery.getResultList();
		return new LinkedHashSet<Thesis>(typedQuery.getResultList());
	}
	
	@Override
	public Set<Thesis> searchByTerm(String term) throws DataAccessException {
		Query query = createQuery("from Thesis myThesis where myThesis.title like ?1 or myThesis.abstract_ like ?1", -1, -1, "%"+term+"%");
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

	/**
	 * Used to determine whether or not to merge the entity or persist the entity when calling Store
	 * @see store
	 * 
	 *
	 */
	public boolean canBeMerged(Thesis entity) {
		return true;
	}

	@Override
	public Set<Thesis> searchThesisByStudentId(String id) throws DataAccessException {
		Query query = createQuery("select myThesis from Thesis myThesis join myThesis.student as myStudent where myStudent.studentId = ?1", -1, -1, Integer.parseInt(id));
		return new LinkedHashSet<Thesis>(query.getResultList());
	}
	
	@Override
	public Set<Thesis> searchThesisByChairId(String id) throws DataAccessException {
		Query query = createQuery("select myThesis from Thesis myThesis join myThesis.thesiscommittees as mythesiscommittee where mythesiscommittee.facultyByChair.facultyId = ?1", -1, -1, Integer.parseInt(id));
		return new LinkedHashSet<Thesis>(query.getResultList());
	}
	
	
	@Override
	public Set<Thesis> searchThesisByCategoryId(int id) throws DataAccessException {
		Query query = createQuery("select myThesis from Thesis myThesis join myThesis.categories as myCategory where myCategory.categoryId = ?1", -1, -1, id);
		return new LinkedHashSet<Thesis>(query.getResultList());
	}

}
